<?php
declare (strict_types=1);

namespace app\services\user;

use think\facade\Env;
use think\facade\Cache;
use think\facade\Event;
use app\dao\user\UserDao;
use think\facade\Snowflake;
use app\services\BaseServices;
use crmeb\services\CacheService;
use crmeb\exceptions\AdminException;
use app\services\user\UserTokenServices;
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};

/**
 *
 * Class UserServices
 * @package app\services\user
 * @mixin UserDao
 */
class UserServices extends BaseServices
{

    /**
     * UserServices constructor.
     * @param UserDao $dao
     */
    public function __construct(UserDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * 微信小程序登录
     * @param array $data
     * @param array $allowedDeviceTypes
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getOpenidLogin(array $data, array $allowedDeviceTypes, string $ip)
    {
        // 设备类型;mobile,android,iphone,ipad,web,pc,mac,wxapp,ios
        if (empty($data['device_type']) || !in_array($data['device_type'], $allowedDeviceTypes)) {
            throw new AdminException('请求错误,未知设备!');
        }

        $token = md5(uniqid()) . md5(uniqid());
        $device_type = $data['device_type'];
        $code = $data['code'];
        $appid = Env::get('MiniProgram.appId');
        $secret = Env::get('MiniProgram.secret');
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid . '&secret=' . $secret . '&js_code=' . $code . '&grant_type=authorization_code';
        $res = file_get_contents($url);
        $res = json_decode($res, true);
        $thr_session = md5($res['openid'] . $res['session_key']);

        $wxapp = [
            'openid' => $res['openid'] ?? '',
            'session_key' => $res['session_key'] ?? '',
            'thr_session' => $thr_session
        ];

        /** @var UserDao $findUser */
        $findUser = $this->dao->getOne(['openid' => $res['openid']]);

        $this->transaction(function () use ($data, $findUser, $ip, $token, $device_type, $res) {


            if (empty($findUser)) {
                // 注册新用户
                $user_id = Snowflake::generate(); // 雪花算法 生成分布式用户ID
                $userData = [
                    'id' => $user_id,
                    'user_type' => 2, // 用户类型;1:admin;2:会员
                    'make_code' => makeCode(), // 自动生成推广码
                    'openid' => $res['openid'] ?? '',
                ];

                if (!$this->dao->save($userData)) {
                    throw new AdminException('网络错误请稍后重试！');
                }

                $count = $this->dao->getCount(['openid' => $res['openid']]);
                if ($count > 1) {
                    throw new AdminException('网络错误请稍后重试！');
                }

            } else {
                $user_id = $findUser->id;
                switch ($findUser['user_status']) {
                    case 0:
                        throw new AdminException('您的账号已被禁用，如有疑问请联系客服');
                    case 2:
                        throw new AdminException('账号还没有验证成功!');
                }
            }

            // 登录完成 记录token
            Event::trigger('user.userToken', [$user_id, $token, $device_type]);
            //登录日志消息队列
            event('user.userLoginLog', [$user_id, 1, $ip, $data['device_type']]);

            return true;
        });

        /** @var UserDao $findUser */
        $findUser = $this->dao->getOne(['openid' => $res['openid']]);
        $user_info = $findUser->toArray();

        return compact('token', 'device_type', 'user_info', 'wxapp');
    }

    /**
     * @param array $data
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getSess(array $data)
    {
        $code = $data['code'];
        $encData = $data['encryptedData'];
        $iv = $data['iv'];
        $appid = Env::get('MiniProgram.appId');
        $secret = Env::get('MiniProgram.secret');
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid . '&secret=' . $secret . '&js_code=' . $code . '&grant_type=authorization_code';
        $res = file_get_contents($url);
        $res = json_decode($res, true);
        $ret = $this->decryUser($iv, $res['session_key'], $encData, $appid);
        $return = json_decode($ret, true);
        $thr_session = md5($res['openid'] . $res['session_key']);

        $wxapp = [
            'user_nickname' => $return['nickName'] ?? '',
            'avatar' => $return['avatarUrl'] ?? '',
            'openid' => $res['openid'] ?? '',
            'session_key' => $res['session_key'] ?? '',
            'thr_session' => $thr_session
        ];

        /** @var UserDao $findUser */
        $findUser = $this->dao->getOne(['openid' => $res['openid']]);

        if (!$findUser) {
            throw new AdminException('用户不存在！');
        }

        $this->dao->update($findUser->id, [
            'user_nickname' => $return['nickName'] ?? '',
            'avatar' => $return['avatarUrl'] ?? ''
        ]);

        return $wxapp;
    }

    /**
     * 微信小程序消息解密
     * @param $iv
     * @param $sess_key
     * @param $encData
     * @param $appid
     * @return mixed
     */
    private function decryUser($iv, $sess_key, $encData, $appid)
    {
        $pc = new \WXBizDataCrypt($appid, $sess_key);
        $errCode = $pc->decryptData($encData, $iv, $data);
        return $data;
    }

}
